/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.baatar.opt.pga.impl.service;

import java.util.ArrayList;
import java.util.Arrays;
import org.apache.log4j.Logger;
import org.baatar.opt.genetic.AlgorithmParameters;
import org.baatar.opt.genetic.GeneticIterationListener;
import org.baatar.opt.genetic.GeneticOptimizer;
import org.baatar.opt.genetic.chromosome.IChromosome;
import org.baatar.opt.genetic.chromosome.MigrantChromosome;
import org.baatar.opt.pga.intf.IPGACluster;
import org.baatar.remoting.service.IImplementor;
import org.baatar.remoting.service.RemotingService;
import org.baatar.remoting.service.RemotingServiceInfo;
import org.baatar.util.GarbageUtil;

/**
 *
 * @author Ba
 */
public class PGAClusterServiceImpl<T, M> implements IImplementor, IPGACluster<T, M> {
    private RemotingService remotingService = null;
    private Logger logger = null;

    private int clusterIndex = 0;   // Index # of the cluster for coordinator.

    private int numOfWorkers = 1;   //  # of genetic optimizers will run parallel.
    private AlgorithmParameters<T, M> algoParams = null;

//    private IPGACoordinator<T, M> coordinator = null;
    private IPGACluster<T, M> clusters[] = new IPGACluster[0];

    public void addNeighbor(IPGACluster<T, M> cluster) {
        clusters = Arrays.copyOf(clusters, clusters.length + 1);
        clusters[clusters.length - 1] = cluster;
    }

    private ArrayList<GeneticOptimizer<T, M>> optimizers = null;

    private boolean optimizationInProgress = false;

    @Override
    public boolean initialize() throws Exception {
        return true;
    }

    @Override
    public void uninitialize() throws Exception {
    }

    @Override
    public boolean start() {
        return true;
    }

    @Override
    public boolean stop() {
        return true;
    }

    private class ProgressMessage {
        int optimizerIndex = 0;
        int iteration = 0;
        String info = null;
        public ProgressMessage(int optimizerIndex,
                                int iteration,
                                String info) {
            this.optimizerIndex = optimizerIndex;
            this.iteration = iteration;
            this.info = info;
        }
    }

//    @Override
//    public Integer registerCoordinator(RemotingServiceInfo coordinatorNetInfo, Integer clusterIndex, AlgorithmParameters<T, M> algoParams) throws Exception {
//        if (!optimizationInProgress) {
//            this.algoParams = algoParams;
//            PGACoordinatorConnection<T, M> coord = new PGACoordinatorConnection<T, M>();
//            coord.setHostName(coordinatorNetInfo.getHostName());
//            coord.setPortNo(coordinatorNetInfo.getListenerPort());
//            coord.setCallerInfo(remotingService.getRemotingServiceInfo().toString());
//
//            this.coordinator = coord;
//
//            this.clusterIndex = clusterIndex;
//            return numOfWorkers;
//        }
//        return 0;
//    }

    @Override
    public RemotingServiceInfo getClusterNetInfo() throws Exception {
        if (remotingService != null)
            return remotingService.getRemotingServiceInfo();
        return null;
    }

    @Override
    public Integer prepareOptimizers() throws Exception {
        int numOfOptPrepared = 0;
        if (!optimizationInProgress) {
            if (optimizers == null)
                optimizers = new ArrayList<GeneticOptimizer<T, M>>();

            for (int i = 0; i < optimizers.size(); i++) {
                optimizers.get(i).uninitialize();
            }
            optimizers.clear();

            algoParams.getChromosomeFactory().init();

            GarbageUtil.runGarbage();
            for (int i = 0; i < numOfWorkers; i++) {
                GeneticOptimizer<T, M> go = new GeneticOptimizer<T, M>();

                go.setOptimizerIndex(i);

                go.setPreferences(algoParams);

                go.setGeneticIterationListener(new GeneticIterationListener<T, M>() {
                    @Override
                    public void onProgress(int optimizerIndex, int iteration, double elapsedTime, String info) {
                        try {
//                            sendProgressMessage(optimizerIndex, iteration, info);
                            logger.info("(" + clusterIndex + "," + optimizerIndex + "): " + iteration + " - " + elapsedTime + " sn; " + info);
                        } catch(Exception ex) {
                            logger.error(ex.getMessage(), ex);
                        }
                    }

                    @Override
                    public void onMigrationNeeded(int optimizerIndex, int iteration, IChromosome<T, M> migrants[]) {
                        try {
                            MigrantChromosome immigrants[] = new MigrantChromosome[migrants.length];
                            for (int i = 0; i < immigrants.length; i++)
                                immigrants[i] = migrants[i].extractMigrant(clusterIndex, optimizerIndex);

//                            coordinator.migrate(clusterIndex,
//                                                optimizerIndex,
//                                                iteration,
//                                                immigrants);

                            for (int i = 0; i < clusters.length; i++) {
                                clusters[i].migrate(immigrants);
                            }

//                            Arrays.copyOf(population, iteration, newType) 
//                            logger.info("(" + clusterIndex + "," + optimizerIndex + "): " + iteration + " - " + best.getFitness());
                        } catch (Exception ex) {
                            logger.error(ex.getMessage(), ex);
                        }
                    }

                    @Override
                    public void onException(int optimizerIndex, String exceptionMessage) {
                    }
                });

                optimizers.add(go);
                logger.info("Optimizer " + i + " is generated.");
                numOfOptPrepared++;
            }
        }
        return numOfOptPrepared;
    }

    @Override
    public Integer runOptimizers() throws Exception {
        int numOfOptStarted = 0;
        if (!optimizationInProgress) {
            for (int i = 0; i < optimizers.size(); i++) {
                if (optimizers.get(i).initialize()) {
                    optimizers.get(i).start();
                    numOfOptStarted++;
                }
            }
        }
        return numOfOptStarted;
    }

    @Override
    public void waitForOptimizers() throws Exception {
        if (!optimizationInProgress) {
            for (int i = 0; i < optimizers.size(); i++) {
                optimizers.get(i).waitForFinish();
            }
        }
    }

    @Override
    public void migrate(MigrantChromosome<M>[] migrants) throws Exception {
        for (int i = 0; i < optimizers.size(); i++) {
            if ((clusterIndex != migrants[0].getClusterIndex())
                    || (i != migrants[0].getOptimizerIndex())) {
                optimizers.get(i).addMigrants(migrants);
//                logger.info("(" + clusterIndex + "," + i + "): " + migrants.length + "#, " + migrants[0].getFitness());
            }
        }
    }

//    @Override
//    protected boolean initializeManager() throws Exception {
//        return true;
//    }

//    @Override
//    protected void finalizeManager() throws Exception {
//    }

//    private LinkedBlockingQueue<ProgressMessage> progressMessageQueue = new LinkedBlockingQueue<ProgressMessage>();
//
//    private void sendProgressMessage(int optimizerIndex, int iteration, String message) {
//        progressMessageQueue.add(new ProgressMessage(optimizerIndex, iteration, message));
//    }

//    @Override
//    protected void goAhead() throws Exception {
//        while (!processCancelled()) {
//            ProgressMessage progressMessage = progressMessageQueue.take();
//            coordinator.reportProgress(clusterIndex,
//                                        progressMessage.optimizerIndex,
//                                        progressMessage.iteration,
//                                        progressMessage.info);
//        }
//    }

//    @Override
//    protected void statusIsBeingChanged(ThreadStatus oldThreadStatus, ThreadStatus newThreadStatus) {
//    }

//    @Override
//    protected void statusChanged(ThreadStatus oldThreadStatus, ThreadStatus newThreadStatus) {
//    }

    /*
     * {@inheritDoc}
     */
    @Override
    public void setRemotingService(RemotingService remotingService) {
        this.remotingService = remotingService;
    }

    /*
     * {@inheritDoc}
     */
    @Override
    public void setLogger(Logger logger) {
        this.logger = logger;
    }

//    /**
//     * @return the coordinator
//     */
//    public IPGACoordinator<T, M> getCoordinator() {
//        return coordinator;
//    }

//    /**
//     * @param coordinator the coordinator to set
//     */
//    public void setCoordinator(IPGACoordinator<T, M> coordinator) {
//        this.coordinator = coordinator;
//    }

    /**
     * @return the numOfWorkers
     */
    public int getNumOfWorkers() {
        return numOfWorkers;
    }

    /**
     * @param numOfWorkers the numOfWorkers to set
     */
    public void setNumOfWorkers(int numOfWorkers) {
        this.numOfWorkers = numOfWorkers;
    }

    /**
     * @return the algoParams
     */
    public AlgorithmParameters<T, M> getAlgoParams() {
        return algoParams;
    }

    /**
     * @param algoParams the algoParams to set
     */
    public void setAlgoParams(AlgorithmParameters<T, M> algoParams) {
        this.algoParams = algoParams;
    }

    /**
     * @return the clusterIndex
     */
    public int getClusterIndex() {
        return clusterIndex;
    }

    /**
     * @param clusterIndex the clusterIndex to set
     */
    public void setClusterIndex(int clusterIndex) {
        this.clusterIndex = clusterIndex;
    }
}
